package com.yeskery.nut.cloud.registry.redis;

import com.yeskery.nut.application.ServerEventContext;
import com.yeskery.nut.bean.ApplicationContext;
import com.yeskery.nut.bean.aware.ApplicationContextAware;
import com.yeskery.nut.cloud.registry.core.*;
import com.yeskery.nut.core.Environment;
import com.yeskery.nut.extend.redis.JedisConnectionPool;
import com.yeskery.nut.extend.responsive.ResponsiveConvert;
import com.yeskery.nut.util.StringUtils;
import redis.clients.jedis.Jedis;

import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Logger;

/**
 * redis服务注册器
 * @author YESKERY
 * 2024/1/12
 */
public class RedisServerRegistry extends DefaultServerRegistry implements ApplicationContextAware {

    /** 日志对象 */
    private static final Logger logger = Logger.getLogger(RedisServerRegistry.class.getName());

    /** 服务hash key */
    private static final String REDIS_SERVERS_HASH_KEY = "nut:cloud:registry:servers";

    /** 服务实例hash key */
    private static final String REDIS_SERVER_INSTANCES_HASH_KEY = "nut:cloud:registry:server:%s";

    /** 定时器 */
    private Timer timer;

    /** jedis客户端 */
    private Jedis jedis;

    /** 应用上下文 */
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public void afterStart(ServerEventContext serverEventContext) {
        Environment environment = applicationContext.getBean(Environment.class);
        ResponsiveConvert responsiveConvert = applicationContext.getBean(ResponsiveConvert.class);
        ServerMetadata serverMetadata = getCurrentServerMetadata(environment);
        ServerInstanceMetadata serverInstanceMetadata = getCurrentServerInstanceMetadata(environment);

        JedisConnectionPool jedisConnectionPool = applicationContext.getBean(JedisConnectionPool.class);
        jedis = jedisConnectionPool.getCacheConnection();
        jedis.hsetnx(REDIS_SERVERS_HASH_KEY, serverMetadata.getName(), responsiveConvert.convertTo(serverMetadata));
        jedis.hsetnx(String.format(REDIS_SERVER_INSTANCES_HASH_KEY, serverMetadata.getName()),
                String.valueOf(serverInstanceMetadata.hashCode()), responsiveConvert.convertTo(serverInstanceMetadata));

        for (Map.Entry<String, String> entry : jedis.hgetAll(REDIS_SERVERS_HASH_KEY).entrySet()) {
            Server server = registerServer(responsiveConvert.convertFromString(entry.getValue(), DefaultServerMetadata.class));
            for (String value : jedis.hgetAll(String.format(REDIS_SERVER_INSTANCES_HASH_KEY, entry.getKey())).values()) {
                server.getServerInstanceRegistry().registerInstance(responsiveConvert.convertFromString(value, DefaultServerInstanceMetadata.class));
            }
        }

        timer = new Timer("redisServerRegistryAsyncTimer", true);
        timer.schedule(new RedisServerAsyncPullTimerTask(), 30, 60000);
    }

    @Override
    public void beforeClose(ServerEventContext serverEventContext) {
        super.beforeClose(serverEventContext);
        if (timer != null) {
            timer.cancel();
        }
        if (jedis != null) {
            JedisConnectionPool jedisConnectionPool = applicationContext.getBean(JedisConnectionPool.class);
            jedisConnectionPool.free(jedis);
        }
    }

    /**
     * 默认redis同步拉取定时任务
     * @author YESKERY
     * 2024/1/14
     */
    private class RedisServerAsyncPullTimerTask extends TimerTask {

        @Override
        public void run() {
            ResponsiveConvert responsiveConvert = applicationContext.getBean(ResponsiveConvert.class);
            Environment environment = applicationContext.getBean(Environment.class);
            String applicationName = getCurrentApplicationName(environment);
            String serverValue = jedis.hget(REDIS_SERVERS_HASH_KEY, applicationName);
            Server server = null;
            if (StringUtils.isEmpty(serverValue)) {
                server = registerServer(getCurrentServerMetadata(environment));
            }

            ServerInstanceMetadata currentServerInstanceMetadata = getCurrentServerInstanceMetadata(environment);
            String serverInstanceValue = jedis.hget(String.format(REDIS_SERVER_INSTANCES_HASH_KEY, applicationName),
                    String.valueOf(currentServerInstanceMetadata.hashCode()));
            if (StringUtils.isEmpty(serverInstanceValue)) {
                if (server == null) {
                    server = getServer(applicationName);
                }
                DefaultServerInstanceMetadata serverInstanceMetadata = responsiveConvert.convertFromString(serverInstanceValue, DefaultServerInstanceMetadata.class);
                server.getServerInstanceRegistry().registerInstance(serverInstanceMetadata);
            }

            for (Map.Entry<String, String> entry : jedis.hgetAll(REDIS_SERVERS_HASH_KEY).entrySet()) {
                DefaultServerMetadata serverMetadata = responsiveConvert.convertFromString(entry.getValue(), DefaultServerMetadata.class);
                server = getServer(serverMetadata.getName());
                if (server == null) {
                    server = registerServer(serverMetadata);
                } else {
                    DefaultServerMetadata defaultServerMetadata = (DefaultServerMetadata) server.getMetadata();
                    String version;
                    if ((version = server.getMetadata().getVersion()) != null && version.equals(defaultServerMetadata.getVersion())) {
                        logger.info("RedisServerRegistryAsync Server[" + serverMetadata.getName() + "] Original Version["
                                + serverMetadata.getVersion() + "] Local Version[" + defaultServerMetadata.getVersion()
                                + "] Not Match, Skip Update.");
                        continue;
                    }
                    defaultServerMetadata.setLatestUpdateTime(System.currentTimeMillis());
                }
                ServerInstanceRegistry serverInstanceRegistry = server.getServerInstanceRegistry();
                for (String value : jedis.hgetAll(String.format(REDIS_SERVER_INSTANCES_HASH_KEY, entry.getKey())).values()) {
                    DefaultServerInstanceMetadata serverInstanceMetadata = responsiveConvert.convertFromString(value, DefaultServerInstanceMetadata.class);
                    if (!serverInstanceRegistry.contains(serverInstanceMetadata)) {
                        serverInstanceRegistry.registerInstance(serverInstanceMetadata);
                    }
                }
            }
        }
    }
}
